package com.hoh.phonelocation.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.hoh.phonelocation.exception.BusinessException;
import com.hoh.phonelocation.exception.ErrorCode;
import com.hoh.phonelocation.exception.ThrowUtils;
import com.hoh.phonelocation.model.entity.PhoneInfo;
import com.hoh.phonelocation.model.response.GetPhoneLocationResponse;
import com.hoh.phonelocation.model.vo.PhoneLocationVO;
import com.hoh.phonelocation.service.PhoneInfoService;
import com.hoh.phonelocation.mapper.PhoneInfoMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.regex.Pattern;

/**
* @author hongzizhou
* @description 针对表【phone_info】的数据库操作Service实现
* @createDate 2025-03-02 14:46:00
*/
@Service
@Slf4j
public class PhoneInfoServiceImpl extends ServiceImpl<PhoneInfoMapper, PhoneInfo>
    implements PhoneInfoService{

    private static final String URL = "https://cx.shouji.360.cn/phonearea.php?number=%s";

    /**
     * 本地缓存
     */
    private final Cache<String, String> LOCAL_CACHE = Caffeine.newBuilder()
            .initialCapacity(1024)
            .maximumSize(10_000L) //最大一万条
            .expireAfterWrite(Duration.ofMinutes(5)) //缓存 5 分钟后移除
            .build();

    @Override
    public PhoneLocationVO getPhoneLocation(String phone) {
        // 从本地缓存查询是否存在该电话信息
        String cachedValue = LOCAL_CACHE.getIfPresent(phone);
        if(cachedValue != null) {
            System.out.println(1);
            // 如果缓存命中，返回结果
            return JSONUtil.toBean(cachedValue, PhoneLocationVO.class);
        }
        // 本地缓存无则查询数据库
        PhoneLocationVO phoneLocationVO = new PhoneLocationVO();

        PhoneInfo phoneInfo = this.lambdaQuery()
                .eq(PhoneInfo::getNumber, phone)
                .one();
        if (phoneInfo != null){
            System.out.println(2);
            phoneLocationVO = PhoneLocationVO.objToVo(phoneInfo);
            // 写入本地缓存
            LOCAL_CACHE.put(phone, JSONUtil.toJsonStr(phoneLocationVO));
            return phoneLocationVO;
        }
        //数据库中没有该电话号码的信息再使用接口进行查询
        //校验电话号是否合法
        System.out.println(3);
        boolean bool = validPhoneNumber(phone);
        ThrowUtils.throwIf(!bool, ErrorCode.PARAMS_ERROR,"手机号非法");

        GetPhoneLocationResponse.Location result = getPhoneLocationByInterface(phone);
        ThrowUtils.throwIf(result == null, ErrorCode.OPERATION_ERROR,"获取电话号归属地失败");

        phoneInfo = new PhoneInfo();
        BeanUtil.copyProperties(result,phoneInfo);
        phoneInfo.setNumber(phone);

        boolean saved = this.save(phoneInfo);
        ThrowUtils.throwIf(!saved, ErrorCode.OPERATION_ERROR,"保存电话号归属地失败");
        // 更新缓存
        phoneLocationVO = PhoneLocationVO.objToVo(phoneInfo);
        LOCAL_CACHE.put(phone, JSONUtil.toJsonStr(phoneLocationVO));
        return phoneLocationVO;
    }

    @Override
    @Retryable(
            retryFor = {IORuntimeException.class},  // 匹配 Hutool 封装的异常
            maxAttempts = 2,
            backoff = @Backoff(delay = 100)
    )
    public GetPhoneLocationResponse.Location getPhoneLocationByInterface(String phone) {

        String url = String.format(URL,phone);
        HttpRequest httpRequest = HttpRequest.get(url).timeout(3000);
        // 处理响应
        try (HttpResponse httpResponse = httpRequest.execute()) {

            GetPhoneLocationResponse getPhoneLocationResponse = JSONUtil.toBean(httpResponse.body(), GetPhoneLocationResponse.class);
            if (getPhoneLocationResponse.getCode() != 0) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "获取电话号归属地失败");
            }
            return getPhoneLocationResponse.getData();
        }
    }

    @Recover
    public void recover(String phone) {
        log.error("重试耗尽，phone={}", phone);
    }

    @Override
    public boolean validPhoneNumber(String phoneNumber) {
        if ((phoneNumber != null) && (!phoneNumber.isEmpty())) {
            return Pattern.matches("^1[3-9]\\d{9}$", phoneNumber);
        }
        return false;
    }

}




